#ifndef _NATE_XML_H_
#define _NATE_XML_H_

#ifdef _MFC_VER
#define _USE_MFC_CSTRING
#define xml_base_string	CString
#else
#include <string>
#define xml_base_string	string
#endif

#pragma warning( disable : 4786 )

#include <list>
#include <vector>
using namespace std;

class INateXMLNode;


enum EKeyType
{
	ePUBLIC_KEY = 0,
	ePRIVATE_KEY

};

class INateXMLAttribute
{
public:
	// property
	__declspec(property(get=GetnodeName))
	xml_base_string nodeName;
	
	__declspec(property(get=Gettext,put=Puttext))
	xml_base_string text;

	virtual xml_base_string GetnodeName() = 0;

	virtual xml_base_string Gettext() = 0;
	virtual void Puttext(xml_base_string text) = 0;

	virtual INateXMLAttribute * getNextItem() = 0;
};


class INateXMLNodeList
{
public:
	// property
	
    __declspec(property(get=Getitem))
    INateXMLNode * item[];

	__declspec(property(get=Getlength))
	int length;


	virtual int Getlength() const = 0;
	virtual INateXMLNode * Getitem(int nIndex) = 0;
		
};

class INateXMLNode
{
public:
	// property
	__declspec(property(get=GetnodeName))
	xml_base_string nodeName;

	__declspec(property(get=Getxml))
	xml_base_string xml;
	
	__declspec(property(get=Gettext,put=Puttext))
	xml_base_string text;

	virtual xml_base_string GetnodeName() = 0;

	virtual xml_base_string Getxml() = 0;

	virtual xml_base_string Gettext() = 0;
	virtual void Puttext(xml_base_string text) = 0;

	virtual INateXMLAttribute* getAttribute(LPCTSTR szAttrName) = 0;
	virtual INateXMLAttribute* traverseAttribute(INateXMLAttribute * pAttr) = 0;

	__declspec(property(get=GetparentNode))
    INateXMLNode* parentNode;
    __declspec(property(get=GetchildNodes))
    INateXMLNodeList* childNodes;
    __declspec(property(get=GetfirstChild))
    INateXMLNode* firstChild;
    __declspec(property(get=GetlastChild))
    INateXMLNode* lastChild;
    __declspec(property(get=GetpreviousSibling))
    INateXMLNode* previousSibling;
    __declspec(property(get=GetnextSibling))
    INateXMLNode* nextSibling;
    
	virtual INateXMLNode* GetparentNode() = 0;
	virtual INateXMLNode* GetfirstChild() = 0;
	virtual INateXMLNode* GetlastChild() = 0;

	virtual INateXMLNodeList* GetchildNodes() = 0;
    
	virtual INateXMLNode* GetpreviousSibling() = 0;
	virtual INateXMLNode* GetnextSibling() = 0;
	
	virtual INateXMLNode* selectSingleNode(LPCTSTR szNodeName) = 0;
	virtual INateXMLNodeList* selectNodes(LPCTSTR szNodeName) = 0;

	virtual INateXMLNode* Detach(INateXMLNode* pIDetachNode  , bool bAutoDelete = false) = 0;
	virtual void Attach( INateXMLNode* pNode1  , INateXMLNode* pInsertNode) = 0;
	virtual bool moveChild(LPCTSTR szNodeName, const unsigned int iFrom,  const unsigned int iTo ) = 0;
	virtual bool deleteChild( LPCTSTR szNodeName, const unsigned int iIndex ) = 0;
				 
	virtual INateXMLNode* createChild(LPCTSTR szNodeName, LPCTSTR szText, bool bInsertHead = false) = 0;
	virtual INateXMLAttribute* createAttribute(LPCTSTR szAttrName, LPCTSTR szText) = 0;

	virtual void PrintXml(FILE * pf,UINT nDepth = 0) = 0;
};

typedef INateXMLNode INateXMLDOMElement;

class INateXMLDom
{
public:
	virtual INateXMLNode* getRoot() = 0;
	virtual	xml_base_string GetLastErrorMessage() = 0;
	virtual INateXMLDOMElement* GetdocumentElement ( ) = 0;
	virtual	bool GetpreserveWhiteSpace() = 0;
	virtual	bool PutpreserveWhiteSpace(bool flag) = 0;
	

	__declspec(property(get=GetpreserveWhiteSpace,put=PutpreserveWhiteSpace))
	bool preserveWhiteSpace;

	__declspec(property(get=GetdocumentElement))
    INateXMLDOMElement* documentElement;

	virtual bool loadXML(LPCTSTR szXml)=0;
	virtual bool saveXML(LPCTSTR szFileName, BOOL bEncrypt = FALSE, LPCTSTR szHashKeyName = NULL)=0;	
};

class CNateXMLDom : public INateXMLDom
{
public:
	CNateXMLDom();
	virtual ~CNateXMLDom();

	void Destroy();

	virtual	INateXMLNode* getRoot() { return m_pTopNode; }
	virtual	xml_base_string GetLastErrorMessage();

	__declspec(property(get=GetpreserveWhiteSpace,put=PutpreserveWhiteSpace))
	bool preserveWhiteSpace;

	virtual	bool GetpreserveWhiteSpace() { return m_bPreserveWhiteSpace; }
	virtual	bool PutpreserveWhiteSpace(bool flag) { m_bPreserveWhiteSpace = flag; return m_bPreserveWhiteSpace; }
	
	__declspec(property(get=GetdocumentElement))
    INateXMLDOMElement* documentElement;

    virtual	INateXMLDOMElement* GetdocumentElement () { return m_pTopNode; }

protected:
	bool m_bPreserveWhiteSpace;
		
	class CNateXMLNodeList : public INateXMLNodeList
	{
	public:
		vector<INateXMLNode*> m_vecXMLNode;

		virtual int Getlength() const { return m_vecXMLNode.size(); }
		virtual INateXMLNode * Getitem(int nIndex)			
		{
			return m_vecXMLNode[nIndex];
		}
	};

	class CNateXMLNode : public INateXMLNode
	{
	protected:
		bool m_bIsComment;
		TCHAR m_chQuotation;

		class CNateXMLAttribute : public INateXMLAttribute
		{
		public:

			CNateXMLAttribute() : m_pNext(NULL) {}

			xml_base_string m_strName;
			xml_base_string m_strText;
			CNateXMLAttribute* m_pNext;
			
			
			virtual INateXMLAttribute* getNextItem() { return m_pNext; }
			
			virtual xml_base_string GetnodeName() { return m_strName; }

			virtual xml_base_string Gettext() { return m_strText; }
			virtual void Puttext(xml_base_string text) { m_strText = text; }		
		};

	public:
		CNateXMLNode();
		virtual ~CNateXMLNode();
		void Destroy();
		bool IsComment() { return m_bIsComment; }

		CNateXMLNode* m_pParentNode;

		CNateXMLNode* m_pPrevSibling;
		CNateXMLNode* m_pNextSibling;

		CNateXMLNode* m_pFirstChild;
		CNateXMLNode* m_pLastChild;

		CNateXMLAttribute* m_pFirstAttribute;

		xml_base_string m_strNodeName;
		
	public:
		bool m_bNoTailTag;
		xml_base_string m_strNodeText;

		void AddChild(CNateXMLNode* pNewNode, bool bInsertHead = false);
		void AddAttribute(CNateXMLAttribute* pNewAttr);

		bool parseTagHeaderXml(LPCTSTR szTagHeaderXml, LPCTSTR pEnd, 
							xml_base_string& strErrorMessage, bool bPreserveWhiteSpace);

	public:
		virtual INateXMLNode* selectSingleNode(LPCTSTR szNodeName);
		virtual INateXMLAttribute* getAttribute(LPCTSTR szAttrName);
		
		virtual INateXMLAttribute* traverseAttribute(INateXMLAttribute* pAttr);

		virtual INateXMLNodeList* selectNodes(LPCTSTR szNodeName);

		virtual INateXMLNode* Detach(INateXMLNode* pIDetachNode  , bool bAutoDelete = false);
		virtual void Attach( INateXMLNode* pNode1  , INateXMLNode* pInsertNode);
		virtual bool moveChild(LPCTSTR szNodeName, const unsigned int iFrom, const unsigned int iTo );
		virtual bool deleteChild( LPCTSTR szNodeName, const unsigned int iIndex );

		virtual INateXMLNode* createChild(LPCTSTR szNodeName, LPCTSTR szText, bool bInsertHead = false);
		virtual INateXMLAttribute* createAttribute(LPCTSTR szAttrName, LPCTSTR szText);
		
		virtual INateXMLNode* GetparentNode() { return m_pParentNode; }
		virtual INateXMLNode* GetfirstChild() { return m_pFirstChild; }
		virtual INateXMLNode* GetlastChild() { return m_pLastChild; }
		virtual INateXMLNode* GetFirstSelectedChild(LPCTSTR szNodeName, bool &bAll, xml_base_string &strNodeName_last_tok);

		virtual INateXMLNodeList* GetchildNodes() { return selectNodes(_T("*")); }
		
		virtual INateXMLNode* GetpreviousSibling() { return m_pPrevSibling; }
		virtual INateXMLNode* GetnextSibling() { return m_pNextSibling; }
		
		virtual xml_base_string GetnodeName() { return m_strNodeName; }				
		virtual xml_base_string Getxml();
		virtual void PrintXml(FILE * pf,UINT nDepth = 0);
		virtual xml_base_string Gettext();
		virtual void Puttext(xml_base_string text);

	protected:
		list<CNateXMLNodeList*> m_listXMLNodeList;
	};
	

public:
	virtual	bool loadXML(LPCTSTR szXml);	
	virtual	bool saveXML(LPCTSTR szFileName, BOOL bEncrypt = FALSE, LPCTSTR szHashKeyName = NULL );

protected:
	CNateXMLNode* parseSingleNodeXml(LPCTSTR szXml, LPCTSTR& pEnd);

	CNateXMLNode* m_pTopNode;

	xml_base_string m_strLastErrorMessage;

public:
	static xml_base_string get_str_tok(LPCTSTR szSrc, LPCTSTR szSplit, int nIndex);
	
	static xml_base_string EncodeXML(LPCTSTR szSrc);
	static xml_base_string DecodeXML(LPCTSTR szSrc, int len = -1);

	static xml_base_string EncodeXML(xml_base_string strSrc);
	static xml_base_string DecodeXML(xml_base_string strSrc);
};

inline 
xml_base_string CNateXMLDom::GetLastErrorMessage()
{
	return m_strLastErrorMessage;
}

/////////////////////////////////////////////////////////////////////////
//

inline bool IS_WHITESPACE(TCHAR ch)
{
	if (ch == TCHAR(' ') || ch == TCHAR('\n') || ch == TCHAR('\r') || ch == TCHAR('\t'))
		return true;

	return false;
}

inline void SKIP_SPACE (LPCTSTR& p, LPCTSTR pEnd)
{
	while (p < pEnd)
	{
		if (!IS_WHITESPACE(*p))
			return;
		p++;
	}
}

inline LPCTSTR find_space(LPCTSTR p, LPCTSTR pEnd)
{
	while (p < pEnd)
	{
		if (IS_WHITESPACE(*p))
			return p;
		p++;
	}

	return NULL;
}

inline LPCTSTR find_chr(LPCTSTR p, int nChar, LPCTSTR pEnd)
{
	while (p < pEnd)
	{
		if (*p == TCHAR(nChar))
			return p;
		p++;
	}

	return NULL;
}

inline xml_base_string CNateXMLDom::EncodeXML(xml_base_string strSrc)
{
#ifdef _USE_MFC_CSTRING
	return EncodeXML((LPCTSTR)strSrc);
#else
	return EncodeXML(strSrc.c_str());
#endif
}

inline xml_base_string CNateXMLDom::DecodeXML(xml_base_string strSrc)
{
#ifdef _USE_MFC_CSTRING
	return DecodeXML((LPCTSTR)strSrc, strSrc.GetLength());
#else
	return DecodeXML(strSrc.c_str(), strSrc.length());
#endif
}

inline xml_base_string EncodeXML(xml_base_string strSrc)
{
	return CNateXMLDom::EncodeXML(strSrc);
}

inline xml_base_string DecodeXML(xml_base_string strSrc)
{
	return CNateXMLDom::DecodeXML(strSrc);
}

#endif